/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufrgs.jsopa;

class Processor extends Thread {
    // Access to hardware components

    private IntController intCn;
    private Memory mem;
    private Console con;
    private Timer tim;
    private Disco dis;
    // CPU internal components
    private int PC;			// Program Counter
    private int[] IR;			// Instruction Register
    private int[] reg;
    // Kernel is like a software in ROM
    private Kernel kernel;

    public Processor(IntController i, Memory m, Console c, Timer t, Disco d) {
        intCn = i;
        mem = m;
        con = c;
        tim = t;
        dis = d;
        PC = 0;
        IR = new int[4];
        reg = new int[16];
        kernel = new Kernel(i, m, c, t, d, this);
    }
    //Access methods

    public int getPC() {
        return PC;
    }

    public void setPC(int i) {
        PC = i;
    }

    public int[] getReg() {
        return reg;
    }

    public void setReg(int[] r) {
        reg = r;
    }

    @Override
    public void run() {
        StringBuilder stb;
        while (Main.isSystemOn()) {
            try {
                sleep(Main.SLEEP_TIME * 2);// was a tenth of a second
            } catch (InterruptedException e) {
            }
            // read from memory in the address indicated by PC
            int RD = mem.read(PC);
            ++PC;
            IR = decodeRegisters(RD);
            // print CPU status to check if it´s ok
            stb = new StringBuilder();
            stb.append("processor: PC=").append(PC);
            stb.append(" IR=").append(IR[0]).append(" ").append(IR[1]).append(" ").append(IR[2]).append(" ").append(IR[3]);
            if (isSW_INT(IR)) {
                stb.append(" SW INT... ");
                kernel.run(IR[3]);
            } else if (isJMP(IR)) {
                stb.append(" JUMPING to ").append(IR[3]);
                PC = IR[3];
            } else if (isLD(IR)) {
                stb.append(" LOADING data from ").append(IR[3]).append(" to ").append(IR[2]);
                kernel.run(36);// SW Disk interrupt
            } else if (isEND(IR)) {
                stb.append(" SHUTTING DOWN!!!");
                Main.print(stb.toString());
                shutDown();
                break;
            } else {
                stb.append(" ??? ");
            }
            Main.print(stb.toString());
            // advance PC to next instruction
            checkHWInt();// this is ok, the method contents were here from the start
        }
        Main.print("Processor shutting down");
    }

    private void shutDown() {
        Main.setSystemOff();
        dis.roda();// TODO release waiting stuff
        checkHWInt();// in a proper manner
    }

    private boolean isEND(int[] IR) {
        return IR[0] == 'E' && IR[1] == 'N' && IR[2] == 'D';
    }

    private boolean isLD(int[] IR) {
        return IR[0] == 'L' && IR[1] == 'D';
    }

    private boolean isJMP(int[] IR) {
        return IR[0] == 'J' && IR[1] == 'P' && IR[2] == 'A';
    }

    private boolean isSW_INT(int[] IR) {
        return IR[0] == 'I' && IR[1] == 'N' && IR[2] == 'T';
    }

    private int[] decodeRegisters(int RD) {
        int[] tmp = new int[4];
        // break the 32bit word into 4 separate bytes
        tmp[0] = RD >>> 24;
        tmp[1] = (RD >>> 16) & 255;
        tmp[2] = (RD >>> 8) & 255;
        tmp[3] = RD & 255;
        return tmp;
    }

    private void checkHWInt() {
        // Check for Hardware Interrupt and if so call the kernel
        if (intCn.get() != 0) {
            // Call the kernel passing the interrupt number
            kernel.run(intCn.get());
            // This goes here because only HW interrupts are reset in the controller
            // But reseting the interrupt controller might be a task for the Kernel 
            // in a real system.
            intCn.reset();
            intCn.V();
        }
    }
}
